// xllrdb.h - range database
#pragma once
// Uncomment the following line to use features for Excel2007 and above.
//#define EXCEL12
#include "xll/xll.h"

#ifndef CATEGORY
#define CATEGORY _T("RDB")
#endif

typedef xll::traits<XLOPERX>::xcstr xcstr; 
typedef xll::traits<XLOPERX>::xword xword; 

namespace rdb {
	
	inline OPERX formula(const OPERX& o)
	{
		ensure (o.xltype == xltypeStr);

		return ExcelX(xlfConcatenate, OPERX(_T("=")), o);
	}
	inline OPERX bang(const OPERX& o)
	{
		ensure (o.xltype == xltypeStr);

		return ExcelX(xlfConcatenate, OPERX(_T("!")), o);
	}
	// evaluate named range even in from another workbook
	inline OPERX eval(const OPERX& name, const OPERX& dbc = OPERX(_T("")))
	{
		OPERX o;

		if (dbc) {
			o = formula(ExcelX(xlfConcatenate, dbc, bang(name)));
		}
		else {
			o = bang(name);
		}

		o = ExcelX(xlfEvaluate, o);

		return o;
	}

	inline bool is_rdb(const OPERX& o)
	{
		return o.xltype == xltypeMulti && o.val.array.columns == 2 && o[0].xltype == xltypeStr;
	}
	inline bool is_id(const OPERX& o) // generated by UUID
	{
		return o.xltype == xltypeStr && o.val.str[0] == 37 && o.val.str[1] == _T('_');
	}
	inline bool is_defined(const OPERX& o)
	{
		return o.xltype == xltypeStr && ExcelX(xlfGetName, bang(o), OPERX(2)).xltype == xltypeBool;
	}
	inline double is_udf(const OPERX& o)
	{
		if (o.xltype == xltypeStr) {
			OPERX o_ = ExcelX(xlfEvaluate, formula(o));
			if (o_.xltype == xltypeNum)
				return o_.val.num;
		}

		return 0;
	}
	inline bool is_callable(const OPERX& o)
	{
		return o[1].xltype == xltypeStr && o[1].val.str[0] > 0;
	}

	// call UDF using elements of range as arguments
	inline OPERX call(const OPERX& o)
	{
		OPERX x;

		if (o.xltype == xltypeStr) {
			x = eval(o);
		}
		else {
			x = o;
		}

		if (is_rdb(x)) {
			x = ExcelX(xlfIndex, x, MissingX(), OPERX(2));
		}

		// convert x[0] string to regid???
		for (xword i = 1; i < x.size(); ++i) {
			if (x[i].xltype  == xltypeStr) {
				OPERX xi = eval(x[i]); // named range?
				if (xi)
					x[i] = xi;
			}
		}

		return xll::Excelv<XLOPERX>(xlUDF, x.size(), x.begin());
	}

} // namespace rdb
